home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Developer Helper 1: Phil & Dave's Excellent CD
/
Excellent CD HFS.raw
/
Moof
/
Goodies
/
HyperCard Goodies
/
Serial Toolkit
/
Source Code
/
SPortUtil.inc
< prev
next >
Wrap
Text File
|
1988-11-18
|
9KB
|
276 lines
{ Miscellaneous routines used with the serial port XCMDs.
This file is included in the other serial port XCMD source code files -- i.e., it is not compiled and
linked separately.
Copyright © 1987,88 Apple Computer, Inc.
Initial coding 9/87 by Harry Chesley.
}
const
GLOBALNAME = 'SPortGlobals'; { Where to store a handle to our globals. }
MAXPARMBLKS = 4; { Maximum number of outgoing parameter blocks per port. }
WRAPCOLUMN = 75; { The column at which to autowrap, if we are autowrapping. }
MAXWRAP = 30; { The maximum number of characters to carry forward when autowrapping. }
type
{ Serial port global data: }
PortDataType =
record
isOpen: boolean; { Whether the port is open yet. }
portInDev, portOutDev: integer; { The device numbers for input and output. }
byteConfig: integer; { The byte format being used. }
shakes: SerShk; { Flow control, etc. }
sendLFs: boolean; { Whether to send linefeeds after carriage returns. }
doEcho: boolean; { Whether to echo input. }
doEdit: boolean; { Whether to allow backspace editing on input. }
stripIncoming: boolean; { Whether to strip the top bit on incoming bytes. }
stripControls: boolean; { Whether to strip out non-return, non-tab control characters. }
autoWrap: boolean; { Auto-wrap input and output. }
currentColumn: integer; { Column we're about to write into. }
inputBuffer: Ptr; { The input buffer, or nil if there is none allocated. }
parmBlks: array [1..MAXPARMBLKS] of ParmBlkPtr; { Asynch IO blocks (for output). }
end;
OurGlobalHandle = ^OurGlobalPtr;
OurGlobalPtr = ^OurGlobalType;
{ Global data: }
OurGlobalType =
record
selectedPort: SPortSel; { Which port is selected. }
ports: array [SPortSel] of PortDataType; { The port data for each port. }
end;
var
{ Global data. (Note: Although this is called global, it's actually allocated as local to the top-level
XCMD routine, and therefore only stays around for the duration of the XCMD execution. Being
able to think of it as globals, and not having to pass it to each subroutine called, however, is
extremely useful. This is perhaps the best reason for using Pascal rather than C for writing
XCMDs.) }
Globals: OurGlobalHandle; { Global data (this handle is saved in a HyperTalk global between XCMDs). }
ThisSPort: PortDataType; { A temporary copy of the global data for the currently selected port. }
procedure GetStrGlobal(name: str255; var glob: str255);
{ Set glob to the global string specified by name. }
var globHand: Handle;
begin
{ Get the HyperTalk global. }
globHand := GetGlobal(name);
{ Convert it to a Pascal string. }
if globHand = nil then glob := ''
else
begin
ZeroToPas(globHand^,glob);
DisposHandle(globHand);
end;
end;
function GetLongGlobal(name: str255): longInt;
{ Return the global string specified by name, interpreted as a long integer. }
var globStr: str255;
begin
{ Get the HyperTalk global into a Pascal string. }
GetStrGlobal(name,globStr);
{ Convert it to a longInt. }
GetLongGlobal := StrToLong(globStr);
end;
procedure SetStrGlobal(name: str255; glob: str255);
{ Set the global string specified by name to glob. }
var globHand: Handle;
begin
{ Convert the string to a HyperTalk style handle-string. }
globHand := PasToZero(glob);
{ Set the global. }
SetGlobal(name,globHand);
{ Dispose of our copy. }
DisposHandle(globHand);
end;
procedure SetLongGlobal(name: str255; globLong: longInt);
{Set the global string specified by name to a string that represents the number in globLong. }
var globStr: str31;
begin
{ Convert the longInt to a Pascal string. }
globStr := LongToStr(globLong);
{ Set the HyperTalk global to that. }
SetStrGlobal(name,globStr);
end;
procedure SetUpSPortGlobals;
{ Create the handle we use for our global variables, and remember it in a HyperCard global. }
var portIndex: SPortSel;
begin
{ Get the value of the global that holds our globals handle. }
Globals := OurGlobalHandle(GetLongGlobal(GLOBALNAME));
{ If it's empty (which will evaluate to zero or nil), then we need to create it. }
if Globals = nil then
begin
{ Make the handle. }
Globals := OurGlobalHandle(NewHandle(sizeof(OurGlobalType)));
if Globals = nil then Fail('could not allocate global variable space');
{ Default to modem port. }
Globals^^.selectedPort := sPortA;
{ Mark both ports as closed. }
for portIndex := sPortA to sPortB do
with Globals^^.ports[portIndex] do
begin
isOpen := false;
{ Set the device numbers. }
case portIndex of
sPortA:
begin portInDev := -6; portOutDev := -7; end;
sPortB:
begin portInDev := -8; portOutDev := -9; end;
end;
{ Set the default byte format. }
byteConfig := baud1200+stop10+noParity+data8;
{ Set the rest of the port defaults. }
with shakes do
begin
fXOn := 0; fCTS := 0; errs := 0; evts := 0; fInX := 0;
end;
{ Set no auto-linefeed, no echo, no edit, no strip top bits. }
sendLFs := false;
doEcho := false;
doEdit := false;
stripIncoming := true;
stripControls := false;
autoWrap := false;
currentColumn := 1;
{ No input buffer (i.e., use the default port buffer). }
inputBuffer := nil;
end;
{ Store our globals handle in the HyperTalk global, so we can get it later. }
SetLongGlobal(GLOBALNAME,ord4(Globals));
end;
{ Copy the globals for the active port into a more easily accessed location. }
ThisSPort := Globals^^.ports[Globals^^.selectedPort];
end;
procedure EnsureOpenPort;
{ Check if the port is open. If it isn't, then open it now. }
var i: integer;
actualIn, actualOut: integer;
whichPort: SPortSel;
begin
if not ThisSPort.isOpen then
begin
{ Get the port. }
whichPort := Globals^^.selectedPort;
{ Allocate and init a gaggle of parmBlks for use with asynchronous output. }
for i := 1 to MAXPARMBLKS do
begin
Globals^^.ports[whichPort].parmBlks[i] := ParmBlkPtr(NewPtr(sizeof(ParamBlockRec)));
with Globals^^.ports[whichPort].parmBlks[i]^ do
begin
ioCompletion := nil;
ioResult := noErr;
ioRefNum := Globals^^.ports[whichPort].portOutDev;
ioBuffer := nil;
end;
end;
{ Copy the new info into the current port. }
ThisSPort := Globals^^.ports[whichPort];
{ Actually open the port. }
if Globals^^.selectedPort = sPortA then
begin
if (OpenDriver('.AOut',actualOut) <> noErr) or (OpenDriver('.AIn',actualIn) <> noErr) then
Fail('OpenDriver failed');
end
else
begin
if (OpenDriver('.BOut',actualOut) <> noErr) or (OpenDriver('.BIn',actualIn) <> noErr) then
Fail('OpenDriver failed');
end;
with ThisSPort do
begin
{ Be paranoid about making sure everything is opened properly. }
if (actualOut <> portOutDev) or (actualIn <> portInDev) then Fail('openDriver failed');
{ Set the defaults. }
if SerHShake(portInDev,shakes) <> noErr then Fail('SerHShake failed');
if SerHShake(portOutDev,shakes) <> noErr then Fail('SerHShake failed');
if (SerReset(portInDev,byteConfig) <> noErr) or (SerReset(portOutDev,byteConfig) <> noErr) then
Fail('SerReset failed');
Globals^^.ports[Globals^^.selectedPort].isOpen := true;
isOpen := true;
end;
end;
end;
procedure GetStrParm(n: integer; var str: str255);
{ Get the nth parameter into str. }
begin
ZeroToPas(paramPtr^.params[n]^,str);
end;
function GetLongParm(n: integer): longInt;
{ Return the nth parameter string, interpreted as a long integer. }
var str: str255;
begin
ZeroToPas(paramPtr^.params[n]^,str);
GetLongParm := StrToNum(str);
end;
function FindFreeIOBlk: ParmBlkPtr;
{ Find and return a free I/O parameter block. If none are free, return nil. }
var i: integer;
begin
for i := 1 to MAXPARMBLKS do
if ThisSPort.parmBlks[i]^.ioResult <= noErr then
begin
FindFreeIOBlk := ThisSPort.parmBlks[i];
exit(FindFreeIOBlk);
end;
FindFreeIOBlk := nil;
end;
function WaitForFreeIOBlk: ParmBlkPtr;
{ Wait for an I/O block to be free and return it. }
var theBlock: ParmBlkPtr;
begin
repeat theBlock := FindFreeIOBlk until theBlock <> nil;
WaitForFreeIOBlk := theBlock;
end;
procedure WaitForAllFree;
{ Wait for all I/O blocks to free up (i.e., wait for all output to finish). }
var allDone: boolean;
i: integer;
begin
repeat
allDone := true;
for i := 1 to MAXPARMBLKS do
if ThisSPort.parmBlks[i]^.ioResult > noErr then allDone := false;
until allDone;
end;